题目描述:
给你 $n$ 个非负整数 $a_1,a_2,…,a_n$,每个数代表坐标中的一个点 $(i, a_i)$ 。在坐标内画 $n$ 条垂直线,垂直线 $i$ 的两个端点分别为 $(i, a_i)$ 和 $(i, 0)$ 。找出其中的两条线,使得它们与 x
轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器。
示例 1:
1 | 输入:[1,8,6,2,5,4,8,3,7] |
示例 2:
1 | 输入:height = [1,1] |
示例 3:
1 | 输入:height = [4,3,2,1,4] |
示例 4:
1 | 输入:height = [1,2,1] |
提示:
- $n = height.length$
- $2 <= n <= 3 * 10^4$
- $0 <= height[i] <= 3 * 10^4$
链接:
https://leetcode-cn.com/problems/container-with-most-water
题目分析
1.暴力解法
直接对数组进行双层遍历,两个变量分别表示容器的两边下标。那么水容量就是短边乘上容器宽,记录最大容量即可。不出意外,这样的算法超时了。
1 | class Solution { |
时间复杂度:$O(n^2)$,$n$ 表示数组大小。
空间复杂度:$O(1)$。我们只需要常数个变量进行记录。
2.暴力解法改进
考虑到,如果我们遍历的时候,从两边往中间遍历,宽是不断变小的,如果边也在同时变小的话,那么肯定不是最大的容量,对于这样的情况是可以进行剪枝的。于是我们稍微修改了遍历的顺序,容器左壁从左边开始遍历,容器右壁从右边开始遍历。分别记录左右边最大值,小于这个值的情况就可以直接进行剪枝了。内层遍历结束后,我们需要将右壁的最大值归为 0 再进行下一个内层遍历。经过剪枝后刚好能够通过测试,并不优良,因为本质上还是双层遍历。
1 | class Solution { |
时间复杂度:$O(n^2)$,$n$ 表示数组大小。虽然经过剪枝,但本质上还是双层遍历。
空间复杂度:$O(1)$。我们只需要常数个变量进行记录。
3.双指针解法
其实上面的暴力解法改进的思路已经非常接近双指针的思路了。上面的暴力解法都是先确定左壁再遍历右壁进行计算,但实际上,容器高只取决于短的那条边。那么如果我们还是从左右两边往中间遍历,是不是可以按照实际情况选择移动左壁还是右壁呢?实际上我们应该移动短的那边就可以了。因为进行移动的话,容器宽变小了,而容器高却还是取决于短边,移动长边不会增加还可能更少,因此一定不会使容量更大。而移动短边的话,可能提高了容器高的值,就可能使容量增大。所以我们实际上只需要进行一层遍历,两个指针开始时分别在数组两端表示两个容器壁,而后短边不断向对方移动,每次移动的时候判断容积是否变大,变大则更新结果,最后直到两个容器壁相遇则结束。
1 | class Solution { |
时间复杂度:$O(n)$,$n$ 表示数组大小。两个指针从左右两端开始遍历直到相遇,总共只对数组进行了一层遍历。
空间复杂度:$O(1)$。我们只需要常数个变量进行记录。